package com.heima.article.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.ArticleConstants;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import org.omg.CORBA.Object;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class HotArticleServiceImpl implements HotArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void computeHotArticle() {
        // 1. 查询五天内的文章，调用前面写的mapper方法实现
        // 可以使用LocalDateTime的 minusDaya来实现获取五天前日期
        LocalDateTime localDateTime = LocalDateTime.of(2021, 4, 20, 0, 0, 0).minusDays(5);
        List<ApArticle> articleList = apArticleMapper.findArticleListByLast5days(localDateTime);
        if (CollectionUtil.isEmpty(articleList)) {
            return;
        }

        // 2. 计算分值 ApArticle -> HotArticleVo
        List<HotArticleVo> hotArticleVos = computeScore(articleList);

        // 3.分组并缓存
        groupAndCache(hotArticleVos);
    }

    private List<HotArticleVo> computeScore(List<ApArticle> list) {
        // 1. 入参判空
        if (CollectionUtil.isEmpty(list)) {
            return new ArrayList<>();
        }

        // 2. 遍历计算得分，将ApArtcle转为HotArticleVo结构
        List<HotArticleVo> voList = new ArrayList<>();
        for (ApArticle apArticle : list) {
            if (apArticle == null) {
                continue;
            }

            // 2.1 计算得分
            Integer score = 0;

            // 2. 提取评论、喜欢、查看、收藏等数据
            Integer views = apArticle.getViews();
            Integer likes = apArticle.getLikes();
            Integer comment = apArticle.getComment();
            Integer collection = apArticle.getCollection();

            //  3. 根据不同的数据，计算不同的分值，最终累加在一起
            // 1次查看+1，1次喜欢+3，1次评论+5，一次收藏+8
            if (views != null) {
                score += views;
            }

            if (likes != null) {
                score += views * 3;
            }

            if (comment != null) {
                score += comment * 5;
            }

            if (collection != null) {
                score += collection * 8;
            }

            // 4. 将数据组装到HotArticleVo中并返回
            HotArticleVo vo = new HotArticleVo();
            vo.setScore(score);
            BeanUtils.copyProperties(apArticle, vo);

            // 2.2 将包含得分的数据，组装到HotArticleVo集合中

            voList.add(vo);
        }

        // 3. 返回HotArticleVo结合
        return voList;
    }

    private void groupAndCache(List<HotArticleVo> hotArticleVos) {
        if (CollectionUtil.isEmpty(hotArticleVos)) {
            return;
        }

        // 1. 按频道分组缓存
        // 使用CollectionUtil.groupByField 方法对集合进行分组
        // 分组条件是 channelId
        // 参数一：要分组的集合
        // 参数二：用来做分组依据的字段
        // vos: [{"channelId":1, "name":"小黑黑"}, {"channelId":2,"name":"小白白"}, {"channelId":1, "name":"小灰灰"}]
        // [
        //   [
        //     {"channelId":1, "name":"小黑黑"},
        //     {"channelId":1, "name":"小灰灰"}
        //   ] ,
        //   [
        //     {"channelId":2,"name":"小白白"}
        //   ]
        // ]

        List<List<HotArticleVo>> groups = CollectionUtil.groupByField(hotArticleVos, "channelId");

        // 2. 遍历分组，一个分组存成一个set集合
        // set名为  ArticleConstants.LOADTYPE_LOAD_MORE + 频道id
        for (List<HotArticleVo> group : groups) {
            if (CollectionUtil.isEmpty(group)) {
                continue;
            }

            Integer channelId = group.get(0).getChannelId();

            // 2.1 使用流的方式进行排序，根据分值从大到小排序
            // sorted(Comparator.comparing(用来排序的字段).reversed())方法可以进行排序
            List<HotArticleVo> collect = group.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());

            // 2.2 使用截取30条信息
            collect = collect.subList(0, collect.size() >= 30 ? 30 : collect.size());

            // 2.3 使用Redis字符串类型进行存储
            redisTemplate.opsForValue().set(ArticleConstants.LOADTYPE_LOAD_MORE + channelId + "", JSON.toJSONString(collect));
        }


        // 4.推荐分一组
        // 分组名为 ArticleConstants.LOADTYPE_LOAD_MORE + ArticleConstants.DEFAULT_TAG
        List<HotArticleVo> recommend = hotArticleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
        recommend = recommend.subList(0, Math.min(recommend.size(), 30));
        redisTemplate.opsForValue().set(ArticleConstants.LOADTYPE_LOAD_MORE + ArticleConstants.DEFAULT_TAG + "", JSON.toJSONString(recommend));
    }


}